import java.util.ArrayList;

import MapLogic.CTowerDefenseMapLogic;
import Properties.props;

import Animation.CTDDamageTC;
import Exceptions.InvalidArgument;
import GameElements.*;
import GameLogic.CTDWave;


public class CTDGameLogic {	
	private int gameTime; //in Sekunden
	private int cycletime;
	private ArrayList<CTDCreep> creeps  = new ArrayList<CTDCreep>();
	private ArrayList<CTDCreep> deadcreeps  = new ArrayList<CTDCreep>();
	private ArrayList<CTowerDefenseElement> tower  = new ArrayList<CTowerDefenseElement>();
	private CTDDamageTC animationofdamage=new CTDDamageTC();
	private int gold;
	private int uniqueIDCounter=0;
	private props constants = new props();
	private CTDWave wave = new CTDWave();
	private CTowerDefenseMapLogic inMap;
	
	public CTDGameLogic() {
		gold = constants.startGold;
	}
	public int getGold() {
		return gold;
	}

	public int getCycle() {
		return cycletime;
	}

	public void setCycle(int cycle) {
		this.cycletime = cycle;
	}

	public int getGameTime() {
		return gameTime;
	}

	public void setGameTime(int gameTime) {
		this.gameTime = gameTime;
	}

	public ArrayList<CTowerDefenseElement> getTower() {
		return tower;
	}

	public void setTower(ArrayList<CTowerDefenseElement> tower) {
		this.tower = tower;
	}

	public ArrayList<CTDCreep> getCreeps() {
		return creeps;
	}

	public void setCreeps(ArrayList<CTDCreep> creeps) {
		this.creeps = creeps;
	}
	
	
	private boolean SellTower(CTowerDefenseElement _tower) throws InvalidArgument 
	{
		if(_tower == null || _tower instanceof CTDCreep)
			throw new InvalidArgument();
		for(int i=0;i<tower.size();i++)
			if(tower.get(i).getUniqueID() == _tower.getUniqueID())
			{
				int money = tower.get(i).getCost();
				int _gold = money *100 / constants.resellPercentage;
				gold += _gold;
				tower.remove(i);
				Synchronize();
				return true;
			}
		return false;
	}	
	
	public boolean UpgradeTower(CTowerDefenseElement _tower) throws InvalidArgument
	{
		if(_tower instanceof CTDCreep ||_tower == null)
			throw new InvalidArgument();
		
		for(int i=0;i<tower.size();i++)
			if(tower.get(i).getUniqueID() == _tower.getUniqueID())
			{
				if(CheckGold(((CTDTower) _tower).getUpgradeCost())==true)
				{
					((CTDTower)_tower).Upgrade();
					gold -= ((CTDTower) _tower).getUpgradeCost();
					Synchronize();
					return true;
				}
				else
					return false;
				
			}
		return false;
	}
	private void Synchronize() {
		inMap.flushMapElements();
		inMap.setTowerOnMap(tower);
		
	}
	public boolean CheckGold(int upgradeCost) {
		int _gold = gold - upgradeCost;
		if(_gold < 0)
			return false;
		return true;
	}

	public boolean BuyTower(CTowerDefenseElement _ctdtower) throws InvalidArgument 
	{
		if(CheckGold(_ctdtower.getCost())==true)
		{				
			if(inMap.checkExTowerOnPlace(_ctdtower.getPosX(), _ctdtower.getPosY()) != false
					&& inMap.checkExTowerOnPlace(_ctdtower.getPosX(), _ctdtower.getPosY()) != true)
				return false;
			tower.add(_ctdtower);
			gold -=  _ctdtower.getCost();
			Synchronize();
			return true;
		}
		else
			return false;
	}
	
	private void BuffNearByTower(CTDBuffTower _ctdtower) {
		ArrayList<CTDTower> nearbytower  = NextAllTower(_ctdtower);
		for(int i=0; i<nearbytower.size();i++)
		{
			nearbytower.get(i).setBuff(_ctdtower.getBuffamount());
		}
		
	}
	
	
	public void RunAction() throws InvalidArgument
	{
		for(int i=0;i<tower.size();i++)
			CalcDamage(tower.get(i));	
	}
	private void AnimDmg(CTDTower elem, CTDCreep _creep, int _dmg) throws InvalidArgument
	{
		animationofdamage.addElement(_creep, elem, _dmg);
	}
	private void CalcDamage(CTowerDefenseElement elem) throws InvalidArgument 
	{
		if(elem instanceof CTDBuffTower)
		{
			if(((CTDBuffTower) elem).checkAttrate() == true)
			{
				BuffNearByTower((CTDBuffTower) elem);
				((CTDBuffTower) elem).updateAttrate();
			}
			return;
		}
		if(((CTDTower) elem).checkAttrate() == false)
			return;
		if(elem instanceof CTDAoET)
		{
			ArrayList<CTDCreep> _creeps  = NextAllTargets((CTDTower) elem);
			if(_creeps.size()<1) return;
			for(int i =0;i<_creeps.size();i++)
			{
				_creeps.get(i).flushSlow();
				ArrayList<CTDImmunity> immune = _creeps.get(i).getImmunity();
				boolean cont = false;
				if(immune.size()<1) 
					for(int j=0;j<immune.size();j++)
					{
						if(((CTDAoET) elem).checkImmunity(immune.get(j)) == true)
						{
							cont = true;
							break;
						}
					}
				if(cont == true) continue;
				
				if(((CTDAoET) elem).getDmgtype() == "slow")
				{
					float walkspeed = ((CTDAoET) elem).getDamage();
					_creeps.get(i).setSlowSpeed(walkspeed);
					return;
				}
				int dmgbytower = ((CTDAoET) elem).getDamage();
				int life = _creeps.get(i).getLP();
				int _lp = life - dmgbytower;
				if(_lp <= 0)
					CreepDead(_creeps.get(i));
				AnimDmg((CTDTower) elem,_creeps.get(i),dmgbytower);
				gold += wave.getGoldPerKill();
				_creeps.get(i).setLP(_lp);
			}
			((CTDAoET) elem).updateAttrate();
			return;
		}
		CTDCreep victim = NextSingleTarget((CTDTower) elem);
		ArrayList<CTDImmunity> immune = victim.getImmunity();
		boolean cont = false;
		if(immune.size()<1) 
			for(int j=0;j<immune.size();j++)
			{
				if(((CTDAoET) elem).checkImmunity(immune.get(j)) == true)
				{
					cont = true;
					break;
				}
			}
		if(cont == true) return;
		int dmgbytower = ((CTDAoET) elem).getDamage();
		int life = victim.getLP();
		int _lp = life - dmgbytower;
		if(_lp <= 0)
			CreepDead(victim);
		AnimDmg((CTDTower) elem,victim,dmgbytower);
		gold += wave.getGoldPerKill();
		((CTDTower) elem).updateAttrate();
		victim.setLP(_lp);

	}

	private void CreepDead(CTDCreep ctdCreep) {
		creeps.remove(ctdCreep);
		deadcreeps.add(ctdCreep);
		
	}
	
	private ArrayList<CTDCreep> pickUpDeadCreeps()
	{
		ArrayList<CTDCreep> temp = new ArrayList<CTDCreep>();
		temp = (ArrayList<CTDCreep>) deadcreeps.clone();
		deadcreeps.clear();
		return temp;
	}
	private CTDCreep NextSingleTarget(CTDTower _tower)
	{
		for(int i=0; i<creeps.size();i++)
		{
			double point = (Math.pow(creeps.get(i).getPosX()-_tower.getPosX(),2) + Math.pow(creeps.get(i).getPosY()-_tower.getPosY(),2));
			double range = Math.pow(_tower.getAttrange(),2);
			if( point <= range)
				return creeps.get(i);
		}
		return null;
	}
	private ArrayList<CTDCreep> NextAllTargets(CTDTower _tower)
	{
		ArrayList<CTDCreep> _creeps  = new ArrayList<CTDCreep>();
		for(int i=0; i<creeps.size();i++)
		{
			double point = (Math.pow(creeps.get(i).getPosX()-_tower.getPosX(),2) + Math.pow(creeps.get(i).getPosY()-_tower.getPosY(),2));
			double range = Math.pow(_tower.getAttrange(),2);
			if( point <= range)
				_creeps.add(creeps.get(i));
		}
		return _creeps;
	}
	private ArrayList<CTDTower> NextAllTower(CTDBuffTower _tower)
	{
		ArrayList<CTDTower> nearbytower  = new ArrayList<CTDTower>();
		for(int i=0; i<tower.size();i++)
		{
			double point = (Math.pow(tower.get(i).getPosX()-_tower.getPosX(),2) + Math.pow(tower.get(i).getPosY()-_tower.getPosY(),2));
			double range = Math.pow(_tower.getBuffrange(),2);
			if( point <= range)
				nearbytower.add(nearbytower.get(i));
		}
		return nearbytower;
	}
	public int getUniqueIDCounter() {
		return uniqueIDCounter;
	}
	public boolean CheckIfBuildAllowed(int PosX, int PosY) throws InvalidArgument
	{
		if(inMap.checkExTowerOnPlace(PosX, PosY) != false
				&& inMap.checkExTowerOnPlace(PosX, PosY) != true)
			return false;
		return true;
	}
	
}
